<?php

class Silk_Application_Subsystem_ModuleLoader implements FP_Application_Subsystem_Interface {

    protected $_config = null;
    protected $_module_paths = null;

    protected static $types = array();

    public function init($pConfig=null){
	$this->_config = $pConfig;

	if (!Silk::Get('DISABLE_MODULE_LOADER')){

	    $this->_module_paths = array(
		(BASE_DIR . 'lib/Silk/Core'),
		(BASE_DIR . 'modules')
	    );

	    $type_classes = array();

	    foreach ($this->_module_paths as $modpath){
		$type_classes = array_merge(
		    $type_classes,
		    $this->scanFolder($modpath)
		);
	    }

	    $this->registerTypes($type_classes);
	}
    }

    protected function scanFolder($pFolder){
	$type_classes = array();

	if (file_exists($pFolder)){

	    $moddir = opendir($pFolder);
	    while ($entry = readdir($moddir)){
		if ($entry != '..' && $entry != '.' && strtolower($entry) != 'vendor' && !preg_match('/^\..*/', $entry) && !preg_match('/^_.*/', $entry)){
		    $modfile = "{$pFolder}/{$entry}";

		    if (is_dir($modfile)){

			$type_classes = array_merge(
			    $type_classes,
			    $this->scanFolder($modfile)
			);

		    } else if (preg_match('/.+\.php/i', $entry)) {
			require_once($modfile);
			$f = new Zend_Reflection_File($modfile);
			$classes = $f->getClasses();

			foreach ($classes as $class){

			    $interfaces = $class->getInterfaces();

			    foreach ($interfaces as $interface){

				if ($interface->getName() == 'Silk_Module_Type_Interface'){

				    $type_classes[] = $class->getName();
				}

			    }

			}
		    }
		}
	    }
	}
	return $type_classes;
    }

    protected function registerTypes(array $pTypeClasses){

	$Types = new Silk_Types();

	foreach ($pTypeClasses as $TypeClass){

	    $ModuleType = new $TypeClass();
// 	    logdebug('Silk_Application_Subsystem_ModuleLoader.registerTypes: Analyzing ' . get_class($ModuleType));
	    self::$types[$ModuleType->getSlug()] = $ModuleType;

	    $existing_version = $Types->getVersionForType($ModuleType->getName());
	    if ($existing_version){

		if ($existing_version < $ModuleType->getVersion())
		    $this->upgradeType($ModuleType);

	    } else {

		$this->installType($ModuleType);
	    }

	    $ModuleType->init();
	}
    }

    protected function upgradeType(Silk_Module_Type_Interface $pType){

	$this->installType($pType);
    }

    protected function installType(Silk_Module_Type_Interface $pType){

	logdebug('Silk_Application_Subsystem_ModuleLoader.installType: Installing ' . $pType->getName());
	$Types = new Silk_Types();

	try {
	    $type = $Types->create(
		array(
		    'name' => $pType->getName(),
		    'slug' => $pType->getSlug(),
		    'description' => $pType->getDescription(),
		    'author' => $pType->getAuthor(),
		    'email' => $pType->getEmail(),
		    'website' => $pType->getWebsite(),
		    'version' => $pType->getVersion()
		),
		true
	    );

	    $meta = $pType->getTypeMeta();

	    foreach ($meta as $m){
		$this->installTypeMeta($m, $type->type_id);
	    }



	} catch (Exception $x){
	    logerr("Silk_Application_Subsystem_ModuleLoader.installType: Failed to install [" . $pType->getName() . "] - Error:" . $x->getMessage() );
	}
    }

    protected function installTypeMeta(Silk_Module_Type_Meta_Interface $pMeta, $pTypeId){
	$Metas = new Silk_TypeMetas();

	$list_values = $pMeta->getValues();

	if (is_array($list_values))
	    $list_values = Zend_Json::encode($list_values);

	$meta = $Metas->create(
	    array(
		'name' => $pMeta->getName(),
		'input_type' => $pMeta->getInputType(),
		'default_value' => $pMeta->getDefaultValue(),
		'list_values' => ($list_values) ?  $list_values : null,
		'validator' => $pMeta->getvalidator(),
		'type_id' => $pTypeId
	    ),
	    true
	);
    }

    public static function GetVirtualType(Silk_Type $pType){
	$ModuleType = new Silk_Module_Virtual_Type($pType);
	return $ModuleType;
    }

    public static function GetType($pTypeSlug){
// 	logdebug('Silk_Application_Subsystem_ModuleLoader.getType: Scanning for [' . $pTypeSlug . '] in - ' . logobject(self::$types));

	if (array_key_exists($pTypeSlug, self::$types))
	    return self::$types[$pTypeSlug];
	else
	    return null;

    }
}

?>